package com.custom.aspect;

import com.custom.annotation.Log;
import com.custom.config.properties.CustomLogProperties;
import com.custom.handle.LogHandle;
import com.custom.handle.impl.DefaultLogHandle;
import com.custom.parser.LogParser;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

@Aspect
@Order(0)
public class CustomLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(CustomLogAspect.class);

    private static final ObjectMapper objectMapper = new ObjectMapper();
    static {
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        objectMapper.registerModule(javaTimeModule);
    }


    @Autowired
    private ApplicationContext factory;

    @Autowired
    private List<LogParser> logParsers;

    @Autowired
    private CustomLogProperties customLogProperties;

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(log)")
    public void logPointcut(Log log) {
    }

    @Around("logPointcut(log)")
    public Object logAround(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new BeanFactoryResolver(factory));
        ExpressionParser parser = new SpelExpressionParser();

        String handle = !StringUtils.isEmpty(log.handle()) ? log.handle() : customLogProperties.getDefaultHandle();

        if (StringUtils.isEmpty(handle)) {
                logger.info("could not found handle");
        } else {
            LogHandle logHandle = (LogHandle)parser.parseExpression(handle).getValue(context);

            String value = null;
            for (LogParser logParser : logParsers) {
                if (logParser.condition(log.type())) {
                    try {
                        value = logParser.parse(joinPoint);
                    } catch (Exception e) {
                        logger.error("failed by logParser", e.getCause());
                    }
                }
            }

            logHandle.worker(value);
        }

        Object result = joinPoint.proceed();
        return result;
    }

}
